import * as vscode from 'vscode';
import * as path from 'path';

class DiagnosticProvider implements vscode.TreeDataProvider<DiagnosticNode> {
	private _onDidChangeTreeData: vscode.EventEmitter<DiagnosticNode | undefined | null | void> = new vscode.EventEmitter<DiagnosticNode | undefined | null | void>();
	readonly onDidChangeTreeData: vscode.Event<DiagnosticNode | undefined | null | void> = this._onDidChangeTreeData.event;

	refresh(): void {
		this._onDidChangeTreeData.fire();
	}

	getTreeItem(element: DiagnosticNode): vscode.TreeItem {
		return element;
	}

	getChildren(element?: DiagnosticNode): Thenable<DiagnosticNode[]> {
		if (element) {
			return Promise.resolve(element.children || []);
		} else {
			// 获取工作区所有的诊断信息
			const allDiagnostics: [vscode.Uri, vscode.Diagnostic[]][] = [];
			
			const document = vscode.window.activeTextEditor?.document;
			if (!document) {
			  return Promise.resolve([]);
			}

			const uri = document.uri;
			const diagnostics = [{
				code: '',
				message: 'cbc-cannot assign twice to immutable variable `x`',
				range: new vscode.Range(new vscode.Position(1, 4), new vscode.Position(1, 10)),
				severity: vscode.DiagnosticSeverity.Error,
				source: '',
				relatedInformation: [
					new vscode.DiagnosticRelatedInformation(new vscode.Location(document.uri, new vscode.Range(new vscode.Position(1, 8), new vscode.Position(1, 9))), 'first assignment to `x`')
				]
			}]


			allDiagnostics.push([uri, diagnostics]);
			// 创建根节点
			const rootNodes: DiagnosticNode[] = [];
			for (const [uri, diagnostics] of allDiagnostics) {
				if (diagnostics.length === 0) {
					continue;
				}

				const fileNode = new DiagnosticNode(
					path.basename(uri.fsPath),
					uri.fsPath,
					vscode.TreeItemCollapsibleState.Expanded
				);
				
				// 设置文件节点的图标
				fileNode.iconPath = new vscode.ThemeIcon('file-code');
				fileNode.contextValue = 'file';
				
				// 为每个文件添加一个命令，点击打开文件
				fileNode.command = {
					command: 'vscode.open',
					arguments: [uri],
					title: 'Open File'
				};

				// 为每个诊断创建子节点
				fileNode.children = diagnostics.map(diagnostic => {
					const severity = this.getSeverityLabel(diagnostic.severity);
					const lineNumber = diagnostic.range.start.line + 1;
					const columnNumber = diagnostic.range.start.character + 1;
					
					// 创建一个新的节点，而不是修改现有节点的collapsibleState属性
					const diagnosticNode = new DiagnosticNode(
						`[${severity}] (${lineNumber}, ${columnNumber}): ${diagnostic.message}`,
						undefined,
						vscode.TreeItemCollapsibleState.Collapsed
					);
					
					// 重新设置命令和图标
					diagnosticNode.command = {
						command: 'diagnostic.showQuickFixes',
						arguments: [
							uri,
							diagnostic.range,
							diagnostic.message
						],
						title: 'Go to Diagnostic and Show Quick Fixes'
					};
					
					// 使节点可点击
					diagnosticNode.contextValue = 'diagnostic';
					
					diagnosticNode.iconPath = this.getDiagnosticIcon(diagnostic.severity);
					
					// 如果有相关信息，添加子节点
					if (diagnostic.relatedInformation && diagnostic.relatedInformation.length > 0) {
						diagnosticNode.children = diagnostic.relatedInformation.map(info => {
							const relatedNode = new DiagnosticNode(
								info.message,
								undefined,
								vscode.TreeItemCollapsibleState.None
							);
							
							// 设置点击行为
							relatedNode.command = {
								command: 'diagnostic.showQuickFixes',
								arguments: [
									info.location.uri,
									info.location.range,
									info.message
								],
								title: 'Go to Related Information and Show Quick Fixes'
							};
							
							// 使节点可点击
							relatedNode.contextValue = 'relatedInformation';
							
							return relatedNode;
						});
					}
					
					return diagnosticNode;
				});
				
				rootNodes.push(fileNode);
			}
			
			return Promise.resolve(rootNodes);
		}
	}
	
	private getSeverityLabel(severity: vscode.DiagnosticSeverity): string {
		switch (severity) {
			case vscode.DiagnosticSeverity.Error:
				return 'Error';
			case vscode.DiagnosticSeverity.Warning:
				return 'Warning';
			case vscode.DiagnosticSeverity.Information:
				return 'Info';
			case vscode.DiagnosticSeverity.Hint:
				return 'Hint';
			default:
				return 'Unknown';
		}
	}
	
	private getDiagnosticIcon(severity: vscode.DiagnosticSeverity): { light: string; dark: string } {
		const basePath = path.join(__dirname, '..', 'resources');
		
		switch (severity) {
			case vscode.DiagnosticSeverity.Error:
				return {
					light: path.join(basePath, 'light', 'error.svg'),
					dark: path.join(basePath, 'dark', 'error.svg')
				};
			case vscode.DiagnosticSeverity.Warning:
				return {
					light: path.join(basePath, 'light', 'warning.svg'),
					dark: path.join(basePath, 'dark', 'warning.svg')
				};
			case vscode.DiagnosticSeverity.Information:
				return {
					light: path.join(basePath, 'light', 'info.svg'),
					dark: path.join(basePath, 'dark', 'info.svg')
				};
			case vscode.DiagnosticSeverity.Hint:
				return {
					light: path.join(basePath, 'light', 'hint.svg'),
					dark: path.join(basePath, 'dark', 'hint.svg')
				};
			default:
				return {
					light: path.join(basePath, 'light', 'info.svg'),
					dark: path.join(basePath, 'dark', 'info.svg')
				};
		}
	}
}

class DiagnosticNode extends vscode.TreeItem {
	children?: DiagnosticNode[];

	constructor(
		public readonly label: string,
		public readonly tooltip?: string,
		public readonly collapsibleState?: vscode.TreeItemCollapsibleState
	) {
		super(label, collapsibleState);
	}
}

export function activate(context: vscode.ExtensionContext) {

	// 创建一个诊断集合,放在一起好管理。如：
	/**
	 * 自动清理：当关联的文件关闭时，VS Code会自动清理其中的诊断信息
	 * 批量管理：可以一次性设置、清除多个诊断
	 * 生命周期管理：诊断集合会随着扩展的激活/停用而自动管理
	 */
	const collection = vscode.languages.createDiagnosticCollection('test');
	if (vscode.window.activeTextEditor) {
		updateDiagnostics(vscode.window.activeTextEditor.document, collection);
	}

	// 对所有打开的文件进行监听
	context.subscriptions.push(vscode.window.onDidChangeActiveTextEditor(editor => {
		if (editor) {
			updateDiagnostics(editor.document, collection);
		}
	}));
	
	// 创建诊断视图
	const diagnosticProvider = new DiagnosticProvider();
	const treeView = vscode.window.createTreeView('diagnosticPanel', {
		treeDataProvider: diagnosticProvider,
		showCollapseAll: true
	});
	
	// 注册自定义悬停提供器
	let lastHoverRange: vscode.Range | undefined;
	let lastHoverMessage: string | undefined;
	
	context.subscriptions.push(
		vscode.languages.registerHoverProvider('*', {
			provideHover(document, position, token) {
				// 检查位置是否在上次指定的范围内
				if (lastHoverRange && lastHoverMessage && lastHoverRange.contains(position)) {
					// 创建带有Markdown格式的悬停提示
					const hoverContent = new vscode.MarkdownString();
					hoverContent.appendMarkdown(`**诊断信息**\n\n${lastHoverMessage}`);
					
					// 添加一些额外的快速修复提示
					hoverContent.appendMarkdown('\n\n---\n\n');
					hoverContent.appendMarkdown('可用修复：\n');
					hoverContent.appendMarkdown('- 声明为可变变量 `mut x`\n');
					hoverContent.appendMarkdown('- 移除第二次赋值\n');
					hoverContent.appendMarkdown('- [了解更多](https://doc.rust-lang.org/error-index.html#E0384)');
					
					// 允许在悬停中显示链接
					hoverContent.isTrusted = true;
					
					return new vscode.Hover(hoverContent, lastHoverRange);
				}
				return null;
			}
		})
	);
	
	// 注册命令处理器，用于处理点击诊断节点的事件
	context.subscriptions.push(
		vscode.commands.registerCommand('diagnostic.showQuickFixes', async (uri: vscode.Uri, range: vscode.Range, message: string) => {
			// 打开文件并跳转到问题所在行
			const document = await vscode.workspace.openTextDocument(uri);
			const editor = await vscode.window.showTextDocument(document);
			editor.selection = new vscode.Selection(range.start, range.end);

			// 将编辑器滚动到指定的文本范围，并使该范围显示在编辑器的中央位置。
			editor.revealRange(range, vscode.TextEditorRevealType.InCenter);
			
			// // 设置最后悬停位置和消息
			// lastHoverRange = range;
			// lastHoverMessage = message;
			
			// 触发VS Code的问题面板和快速修复功能
			// 这会自动显示VS Code原生的问题面板，包含诊断信息和可用的快速修复选项
			await vscode.commands.executeCommand('editor.action.marker.next');
			// await vscode.commands.executeCommand('editor.action.showHover');
			// await vscode.commands.executeCommand('editor.action.quickFix');
		})
	);
	
	// 注册代码操作提供器，为诊断提供quick fix选项
	context.subscriptions.push(
		vscode.languages.registerCodeActionsProvider(
			{ pattern: '**/*.rs' },
			{
				provideCodeActions(document, range, context) {
					const diagnostics = context.diagnostics;
					if (diagnostics.length === 0) {
						return [];
					}
					
					// 为诊断创建代码操作
					const actions: vscode.CodeAction[] = [];
					
					// 添加多个快速修复选项，像图片中那样
					// 选项1: 将变量声明为可变的
					const makeVarMutable = new vscode.CodeAction('声明为可变变量 `mut x`', vscode.CodeActionKind.QuickFix);
					makeVarMutable.edit = new vscode.WorkspaceEdit();
					makeVarMutable.diagnostics = [diagnostics[0]];
					
					// 这里只是一个示例，在真实应用中你需要找到变量声明并修改它
					makeVarMutable.edit.replace(
						document.uri,
						new vscode.Range(new vscode.Position(1, 4), new vscode.Position(1, 5)),
						'mut x'
					);
					
					// 选项2: 移除第二次赋值
					const removeSecondAssignment = new vscode.CodeAction('移除第二次赋值', vscode.CodeActionKind.QuickFix);
					removeSecondAssignment.edit = new vscode.WorkspaceEdit();
					removeSecondAssignment.diagnostics = [diagnostics[0]];
					removeSecondAssignment.edit.delete(
						document.uri,
						new vscode.Range(new vscode.Position(3, 0), new vscode.Position(4, 0))
					);
					
					// 选项3: 更多详情 (这通常会打开文档链接)
					const learnMore = new vscode.CodeAction('了解更多关于 E0384 错误', vscode.CodeActionKind.QuickFix);
					learnMore.command = {
						title: '了解更多',
						command: 'vscode.open',
						arguments: [vscode.Uri.parse('https://doc.rust-lang.org/error-index.html#E0384')]
					};
					learnMore.diagnostics = [diagnostics[0]];
					learnMore.isPreferred = false;
					
					actions.push(makeVarMutable, removeSecondAssignment, learnMore);
					return actions;
				}
			}
		)
	);
	
	// 当诊断信息更新时刷新视图
	context.subscriptions.push(vscode.languages.onDidChangeDiagnostics(() => {
		diagnosticProvider.refresh();
	}));
	
	context.subscriptions.push(treeView);
}

function updateDiagnostics(document: vscode.TextDocument, collection: vscode.DiagnosticCollection): void {
	if (document && path.basename(document.uri.fsPath) === 'sample-demo.rs') {
		collection.set(document.uri, [{
			code: '',
			message: 'cannot assign twice to immutable variable `x`',
			range: new vscode.Range(new vscode.Position(3, 4), new vscode.Position(3, 10)),
			severity: vscode.DiagnosticSeverity.Error,
			source: '',
			relatedInformation: [
				new vscode.DiagnosticRelatedInformation(new vscode.Location(document.uri, new vscode.Range(new vscode.Position(1, 8), new vscode.Position(1, 9))), 'first assignment to `x`')
			]
		}]);
	} else {
		collection.clear();
	}
}
